home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 52 / Amiga Format AFCD52 (Issue 136, May 2000).iso / -in_the_mag- / multitasking / priorities / priman / source / event.c next >
C/C++ Source or Header  |  2000-03-05  |  46KB  |  1,465 lines

  1. /*
  2.  *        Task Priority Manager
  3.  *        Copyright 1993, 1994 Barry McConnell
  4.  *        bmccnnll@tcd.ie
  5.  *
  6.  *        Handle events from the windows.
  7.  
  8.  *        Set tab stops to 4 when editing this file.
  9.  */
  10.  
  11. #include "PriMan.h"
  12.  
  13. /*
  14.  *        Process an input event from the main window.
  15.  */
  16. void HandleMainWindow(ULONG class, WORD code, WORD key, WORD raw, UWORD shift, UWORD ctrl, struct Gadget *selectedGad)
  17.     {
  18.     int                            projectMenu,    /* menu item selected from Project menu            */
  19.                                 taskMenu,        /* menu item selected from Task menu            */
  20.                                 wide;            /* slider is on wide scale                        */
  21.  
  22.     long                        signal = 0;        /* signal type (SIGBREAKF_CTRL_C etc.) to send    */
  23.  
  24.     UWORD                        menuNumber,        /* ordinal menu number selected                 */
  25.                                 gadget;            /* ID of selected gadget                        */
  26.  
  27.     BYTE                        newpri;            /* priority to change task to                    */
  28.  
  29.     BOOL                        frozen;            /* task is frozen                                */
  30.  
  31.     struct MenuItem                *item;            /* menu item we're playing with                    */
  32.  
  33.     /*
  34.      *        The next few variables are used for creating and walking the list of
  35.      *        windows and screens belonging to the task we just killed.
  36.      */
  37.     ULONG                ibase;
  38.     struct List            windowList;
  39.     struct Node            *node, *nextNode;
  40.     struct Remember        *windowKey;
  41.     struct Screen        *screen;
  42.     struct Window        *window;
  43.  
  44.     /*
  45.      *        Here are the requesters that can appear as the user plays around.
  46.      *        The first one is the About requester...
  47.      */
  48.     struct EasyStruct about =
  49.         {
  50.         sizeof(struct EasyStruct),
  51.         0,
  52.         "About PriMan",
  53.         "Task Priority Manager "VERSION"\nFreely Distributable\nCopyright 1993, 1994 Barry McConnell\nbmccnnll@tcd.ie",
  54.         "Okay|Help..."
  55.         };
  56.     
  57.     /*
  58.      *        When you try to Signal, Kill or Freeze PriMan, you get this requester.
  59.      */
  60.     struct EasyStruct suicide =
  61.         {
  62.         sizeof(struct EasyStruct),
  63.         0,
  64.         "PriMan warning",
  65.         "You can't do that to me!",
  66.         "Whoops"
  67.         };
  68.     
  69.     /*
  70.      *        User tried to manipulate a task which no longer exists.
  71.      */
  72.     struct EasyStruct lost =
  73.         {
  74.         sizeof(struct EasyStruct),
  75.         0,
  76.         "PriMan trouble",
  77.         "Task was not found!",
  78.         "Okay"
  79.         };
  80.  
  81.     /*
  82.      *        Confirmation to Break a task.
  83.      */
  84.     struct EasyStruct breakMsg =
  85.         {
  86.         sizeof(struct EasyStruct),
  87.         0,
  88.         "PriMan warning",
  89.         "Really signal `%s'?",
  90.         "Signal|Cancel"
  91.         };
  92.     
  93.     /*
  94.      *        Confirmation to Kill a task.
  95.      */
  96.     struct EasyStruct killMsg =
  97.         {
  98.         sizeof(struct EasyStruct),
  99.         0,
  100.         "PriMan warning",
  101.         "Really remove `%s'?",
  102.         "Remove|Cancel"
  103.         };
  104.     
  105.     /*
  106.      *        Confirmation to close a task's windows.
  107.      */
  108.     struct EasyStruct closeWindows =
  109.         {
  110.         sizeof(struct EasyStruct),
  111.         0,
  112.         "PriMan query",
  113.         "Close windows belonging\nto this task?",
  114.         "Yes|No"
  115.         };
  116.  
  117.     /*
  118.      *        Confirmation to close a task's screens.
  119.      */
  120.     struct EasyStruct closeScreens =
  121.         {
  122.         sizeof(struct EasyStruct),
  123.         0,
  124.         "PriMan query",
  125.         "Close screens belonging\nto this task?",
  126.         "Yes|No"
  127.         };
  128.  
  129.     /*
  130.      *        We come into this procedure after something has happened. We don't
  131.      *        leave until all necessary operations have been completed, and PriMan
  132.      *        is able to go back to sleep (or exit!).
  133.      *
  134.      *        Having menus complicates things somewhat. The user can make multiple
  135.      *        selections, yet we only get one message. So we're going to handle
  136.      *        menus first (setting some action variables), followed by anything
  137.      *        else which only needs to be done once, before finally going through
  138.      *        a list of everything the user can possibly do, and checking to see
  139.      *        if it has been done (both by checking buttons and keypresses, and
  140.      *        examing the action variables set earlier).
  141.      */
  142.     projectMenu = taskMenu = -1;  /* nothing selected so far */
  143.     gadget = class == GADGETUP ? selectedGad -> GadgetID : NULL;  /* if applicable, the gadget released */
  144.  
  145.     /*
  146.      *        Here we process the major once-off events.
  147.      */
  148.     switch (class)
  149.         {
  150.         /*
  151.          *        First up are menus. A design decision to be made is: how do we
  152.          *        handle multi-select? After a lot of consideration, I decided to
  153.          *        let the user select at most one item from each of the two menus.
  154.          *        The last item selected on each is the one we process. All others
  155.          *        (if any) are ignored, with the exception of Wide Slider, which
  156.          *        gets processed as soon as we come across it (even if this means
  157.          *        the slider jumps around many times in a row if the user multi-
  158.          *        selects it a lot in one go). The Frozen checkmark is carefully
  159.          *        set to what it should be later on in the code.
  160.          *
  161.          *        Using the projectMenu and taskMenu variables, we keep track of
  162.          *        what we're going to process later. These simply contain copies
  163.          *        of the ordinal menu item selected.
  164.          */
  165.         case IDCMP_MENUPICK:
  166.             menuNumber = code;  /* first menu item selected */
  167.             while (menuNumber != MENUNULL)
  168.                 {
  169.                 switch (MENUNUM(menuNumber))
  170.                     {
  171.                     case M_PROJECT:
  172.                         projectMenu = ITEMNUM(menuNumber);
  173.                         break;
  174.         
  175.                     case M_TASK:
  176.                         switch (taskMenu = ITEMNUM(menuNumber))
  177.                             {
  178.                             case I_WIDE:
  179.                                 /*
  180.                                  *        The easiest way to make sure the Wide
  181.                                  *        Slider menu item is always in the right
  182.                                  *        state is to update the slider right now.
  183.                                  */
  184.                                 WideSlider(ItemAddress(menuStrip, FULLMENUNUM(M_TASK, I_WIDE, NOSUB))
  185.                                             -> Flags & CHECKED);
  186.                                 break;
  187.  
  188.                             case I_PRIORITY:
  189.                                 /*
  190.                                  *        It's best to handle the Priority sub-
  191.                                  *        menu here, so we know later immediately
  192.                                  *        what priority to change to (stored in
  193.                                  *        the UserData field of the menu item).
  194.                                  */
  195.                                 newpri = (BYTE)GTMENUITEM_USERDATA(ItemAddress(menuStrip, menuNumber));
  196.                                 break;
  197.  
  198.                             case I_SIGNAL:
  199.                                 /*
  200.                                  *        Here we just make a note of which signal
  201.                                  *        will need sending. Again, this info is
  202.                                  *        held in the menu item's UserData field.
  203.                                  */
  204.                                 signal = (long)GTMENUITEM_USERDATA(ItemAddress(menuStrip, menuNumber));
  205.                                 break;
  206.                             }
  207.                         break;
  208.                     }
  209.  
  210.                 /*
  211.                  *        This is how we walk through the menu list in the order
  212.                  *        the user selected them.
  213.                  */
  214.                 menuNumber = ItemAddress(menuStrip, menuNumber) -> NextSelect;
  215.                 }
  216.             break;
  217.  
  218.         /*
  219.          *        If the user clicks the window's close gadget, we do the exact
  220.          *        same thing as if he selected the Hide menu item. Since only one
  221.          *        of these Intuition events can happen at once, it's safe to play
  222.          *        around with a variable that really belongs to the code above.
  223.          */
  224.         case IDCMP_CLOSEWINDOW:
  225.             projectMenu = I_HIDE;
  226.             break;
  227.  
  228.         /*
  229.          *        Obligatory stub when we get this event, to let Intuition know we
  230.          *        don't really care about window refreshing.
  231.          */
  232.         case IDCMP_REFRESHWINDOW:
  233.             GT_BeginRefresh(mainWindow);
  234.             GT_EndRefresh(mainWindow, TRUE);
  235.             break;
  236.  
  237.         /*
  238.          *        There are a few things the user can do which have no menu
  239.          *        equivalents (or don't rely on projectMenu and taskMenu).
  240.          *        These get handled here.
  241.          */
  242.         default:
  243.             /*
  244.              *        Handling the slider gadget is easy - we pretend the Priority
  245.              *        menu item was selected (see the comment for the CLOSEWINDOW
  246.              *        event above), and set newpri appropriately.
  247.              */
  248.             if (gadget == SLIDERGAD)
  249.                 {
  250.                 newpri = code;
  251.                 taskMenu = I_PRIORITY;
  252.                 }
  253.  
  254.             /*
  255.              *        The Wide Slider menu item is the only one that was actually
  256.              *        handled while scanning through the selected menu items. But
  257.              *        the user can also press the Tab key to change the scale, and
  258.              *        we handle that here. If the Wide Slider menu item is enabled
  259.              *        (disabled means either no task has been selected, or the
  260.              *        current priority is already in the wide range), we need to
  261.              *        toggle it, as well as updating the slider gadget.
  262.              */
  263.             item = ItemAddress(menuStrip, FULLMENUNUM(M_TASK, I_WIDE, NOSUB));
  264.             if (key == TAB && item -> Flags & ITEMENABLED)
  265.                 {
  266.                 WideSlider(item -> Flags & CHECKED ? FALSE : TRUE);
  267.                 ClearMenuStrip(mainWindow);
  268.                 item -> Flags ^= CHECKED;
  269.                 ResetMenuStrip(mainWindow, menuStrip);
  270.                 }
  271.  
  272.             /*
  273.              *        To allow PriMan to be fully keyboard-navigatiable, we let
  274.              *        the user move through the task list using the up and down
  275.              *        cursor keys, and change priorites (assuming a task is
  276.              *        currently selected) using the left and right cursor keys.
  277.              *        Holding down the Shift key moves to the extremity of the
  278.              *        task list or slider. We actually cheat here, and fake a
  279.              *        message saying that a certain ListView entry was selected,
  280.              *        so that the next section of code (below) will handle
  281.              *        everything for us!
  282.              */
  283.             if (raw)  /* was a cursor key pressed? */
  284.                 {
  285.                 /*
  286.                  *        If the user presses the cursor left or right keys, we'll
  287.                  *        need to know whether we're on a wide or narrow priority
  288.                  *        scale, so we figure that out now by looking at the menu.
  289.                  */
  290.                 wide = ItemAddress(menuStrip, FULLMENUNUM(M_TASK, I_WIDE, NOSUB)) -> Flags & CHECKED;
  291.  
  292.                 switch (raw)
  293.                     {
  294.                     case CURSORUP:
  295.                         /*
  296.                          *        The first thing we do is always to pretend that
  297.                          *        the ListView gadget itself has been clicked on.
  298.                          */
  299.                         gadget = LISTGAD;
  300.  
  301.                         /*
  302.                          *        If the Shift key has been pressed, we always
  303.                          *        jump to the first entry in the list.
  304.                          */
  305.                         if (shift)
  306.                             code = 0;
  307.  
  308.                         else
  309.                             /*
  310.                              *        We need to handle two special cases here: if
  311.                              *        no entry is selected, we select the first
  312.                              *        one; and if the first entry is selected, we
  313.                              *        do nothing.
  314.                              */
  315.                             switch (pos)
  316.                                 {
  317.                                 case -1:  /* no entry selected */
  318.                                     code = 0;
  319.                                     break;
  320.     
  321.                                 case 0:  /* first entry selected */
  322.                                     gadget = 0;  /* gadget wasn't really clicked on */
  323.                                     break;
  324.     
  325.                                 default:
  326.                                     code = pos - 1;
  327.                                 }
  328.                         break;
  329.  
  330.                     case CURSORDOWN:
  331.                         /*
  332.                          *        This is pretty similar to the previous case,
  333.                          *        except we do nothing if the last entry is
  334.                          *        selected, and jump to the last entry if Shift is
  335.                          *        pressed.
  336.                          */
  337.                         gadget = LISTGAD;
  338.  
  339.                         if (shift)
  340.                             code = taskCount - 1;
  341.                         else if (pos == -1)  /* no entry selected */
  342.                             code = 0;
  343.                         else if (pos == taskCount - 1)  /* last entry selected */
  344.                             gadget = 0;  /* gadget wasn't really clicked on */
  345.                         else
  346.                             code = pos + 1;
  347.                         break;
  348.  
  349.                     case CURSORLEFT:
  350.                         /*
  351.                          *        Now we're working with the slider gadget. If no
  352.                          *        task is selected, we do nothing here. Otherwise,
  353.                          *        we check for the shift key as before, and jump
  354.                          *        to the extremity of the current scale if
  355.                          *        necessary. If it wasn't pressed, we decrement
  356.                          *        the priority as long as it will stay within the
  357.                          *        range of the current scale. We also pretend that
  358.                          *        the Priority menu item was selected, and leave
  359.                          *        it up to some code later to handle this.
  360.                          */
  361.                         if (current)
  362.                             {
  363.                             taskMenu = I_PRIORITY;
  364.  
  365.                             if (shift)
  366.                                 newpri = wide ? -128 : -25;
  367.                             else if (current -> ln_Pri > (wide ? -128 : -25))
  368.                                 newpri = current -> ln_Pri - 1;
  369.                             else
  370.                                 taskMenu = -1;  /* don't want to change priority after all */
  371.                             }
  372.                         break;
  373.             
  374.                     case CURSORRIGHT:
  375.                         /*
  376.                          *        Very similar to the above case, except we're
  377.                          *        incrementing and not decrementing.
  378.                          */
  379.                         if (current)
  380.                             {
  381.                             taskMenu = I_PRIORITY;
  382.  
  383.                             if (shift)
  384.                                 newpri = wide ? 127 : 25;
  385.                             else if (current -> ln_Pri < (wide ? 127 : 25))
  386.                                 newpri = current -> ln_Pri + 1;
  387.                             else
  388.                                 taskMenu = -1;  /* don't want to change priority after all */
  389.                             }
  390.                         break;
  391.                     }
  392.                 }
  393.  
  394.             /*
  395.              *        If the user clicks on an entry in the list, we have to do
  396.              *        quite a lot of work. First, we update the slider gadget to
  397.              *        reflect the priority and move it to the appropriate scale
  398.              *        (narrow if possible). Next, we enable various gadgets and
  399.              *        menu items which are not accessible while no task is
  400.              *        selected. And finally, we adjust the Frozen menu item
  401.              *        checkmark to match the task's state.
  402.              */
  403.             if (gadget == LISTGAD)
  404.                 {
  405.                 /*
  406.                  *        We must make a note of the ordinal number of the
  407.                  *        currently-selected entry in the ListView, and then walk
  408.                  *        through all the entries in the list until we get to that
  409.                  *        ordinal position. It's okay to trash the code variable
  410.                  *        in this loop since we won't need it later.
  411.                  */
  412.                 pos = code;
  413.                 for (current = taskList.lh_Head; code > 0; current = current -> ln_Succ, code --)
  414.                     ;  /* empty body */
  415.                 currentTask = ((struct ListType *)current) -> mainTask;
  416.  
  417.                 /*
  418.                  *        If the user is manipulating the ListView using the
  419.                  *        keyboard, we'll need to actually select the relevant
  420.                  *        entry here (passed down from the cursor-key code above).
  421.                  *        Under V37 we always put this entry at the top of the
  422.                  *        list, but under V39 we can use the MakeVisible tag to
  423.                  *        scroll the ListView when it becomes necessary, which is
  424.                  *        more user-friendly.
  425.                  */
  426.                 if (raw)
  427.                     GT_SetGadgetAttrs(listGad, mainWindow, NULL,
  428.                                                 GTLV_Selected,        pos,
  429.                         osver < 39 ? GTLV_Top :    GTLV_MakeVisible,    pos,
  430.                                                 TAG_END);
  431.  
  432.                 /*
  433.                  *        A separate function makes the necessary changes to the
  434.                  *        slider gadget and enables the relevant menu items.
  435.                  */
  436.                 OnTask();
  437.  
  438.                 /*
  439.                  *        Here we simply enable the Break and Kill buttons.
  440.                  */
  441.                 GT_SetGadgetAttrs(breakGad, mainWindow, NULL,
  442.                                     GA_Disabled,    FALSE,
  443.                                     TAG_END);
  444.                 GT_SetGadgetAttrs(killGad, mainWindow, NULL,
  445.                                     GA_Disabled,    FALSE,
  446.                                     TAG_END);
  447.  
  448.                 /*
  449.                  *        This last piece of code adjusts the Frozen menu item
  450.                  *        checkmark appropriately, by reading its address and
  451.                  *        inspecting the task structure to find out what it
  452.                  *        should be set to. The menu strip is removed from the
  453.                  *        window while the changes are being made.
  454.                  */
  455.                 ClearMenuStrip(mainWindow);
  456.  
  457.                 item = ItemAddress(menuStrip, FULLMENUNUM(M_TASK, I_FROZEN, NOSUB));
  458.                 if (Frozen(currentTask))
  459.                     item -> Flags |= CHECKED;
  460.                 else
  461.                     item -> Flags &= ~CHECKED;
  462.  
  463.                 ResetMenuStrip(mainWindow, menuStrip);
  464.                 }
  465.             break;
  466.         }
  467.  
  468.     /*
  469.      *        Once we've got this far, we have sorted out what menu events need
  470.      *        handling, updated the window's gadgetry to reflect the currently-
  471.      *        selected task, and are left with the possibility of one or both of
  472.      *        projectMenu and taskMenu set. We'll handle the latter first, since
  473.      *        the former may require PriMan's interface to be closed down. In
  474.      *        actual fact, what we do now is group all the different events that
  475.      *        result in the same action (keypress, mouse click, menu selection),
  476.      *        and handle them with one piece of code.
  477.      *
  478.      *        The first thing we'll do is handle the Update menu item, which also
  479.      *        has Space and Return keyboard shortcuts. This one is trivial...
  480.      */
  481.     if (taskMenu == I_UPDATE || key == ' ' || key == RETURN)
  482.         CreateList(&memoryKey);
  483.  
  484.     /*
  485.      *        We handle the Priority menu item here. In actual fact, there are
  486.      *        several places above where taskMenu gets set to this, but what it
  487.      *        amounts to is: as long as the task is still valid, its priority gets
  488.      *        changed to newpri.
  489.      */
  490.     if (taskMenu == I_PRIORITY)
  491.         {
  492.         if (!ValidTask(currentTask))
  493.             {
  494.             SimpleRequest(&lost, NULL);
  495.             CreateList(&memoryKey);
  496.             }
  497.         else
  498.             {
  499.             /*
  500.              *        Changing the priority involves more than just telling Exec
  501.              *        to update it. We also remove the list from the ListView,
  502.              *        make a change to the appropriate entry (using CreateString()
  503.              *        which will join the task's name and new priority together;
  504.              *        we instruct it to overwrite what was originally there),
  505.              *        restore the ListView, and finally update the slider gadget.
  506.              */
  507.             SetTaskPri(currentTask, newpri);
  508.  
  509.             GT_SetGadgetAttrs(listGad, mainWindow, NULL,
  510.                                 GTLV_Labels,    ~0,
  511.                                 TAG_END);
  512.             CreateString(currentTask, current -> ln_Name);
  513.             current -> ln_Pri = newpri;
  514.             GT_SetGadgetAttrs(listGad, mainWindow, NULL,
  515.                                 GTLV_Labels,    &taskList,
  516.                                 GTLV_Selected,    pos,  /* only required under 2.x */
  517.                                 TAG_END);
  518.  
  519.             OnTask();
  520.             }
  521.         }
  522.  
  523.     /*
  524.      *        The Break and Kill options are very similar, at least to start with,
  525.      *        so they share some code. Since there are many scenarios involving
  526.      *        the Break option, we check out that first. If the user used the menu
  527.      *        here, it's easy, since the appropriate signal type is already in the
  528.      *        signal variable. Otherwise, we must check the following:
  529.      *
  530.      *        -  If the Ctrl key is pressed, set the signal variable if a valid
  531.      *           keyboard shortcut is being used.
  532.      *
  533.      *        -  If the Break gadget was pressed, or its own keyboard shortcut was
  534.      *           used, set the signal variable for a Ctrl-C signal.
  535.      */
  536.     if (ctrl && key)
  537.         /*
  538.          *        Signals can be sent to a task simply by holding down the Ctrl
  539.          *        key and pressing C, D, E, or F, and we set the signal variable
  540.          *        appropriately here. Note that the taskMenu variable is actually
  541.          *        not used by the Break code: if signal is set, it is assumed that
  542.          *        taskMenu is too. (So we don't actually need to explicitly set
  543.          *        taskMenu to I_SIGNAL below!)
  544.          */
  545.         switch (key)
  546.             {
  547.             case 'c':
  548.                 signal = SIGBREAKF_CTRL_C;
  549.                 break;
  550.  
  551.             case 'd':
  552.                 signal = SIGBREAKF_CTRL_D;
  553.                 break;
  554.  
  555.             case 'e':
  556.                 signal = SIGBREAKF_CTRL_E;
  557.                 break;
  558.  
  559.             case 'f':
  560.                 signal = SIGBREAKF_CTRL_F;
  561.                 break;
  562.             }
  563.  
  564.     else if (gadget == BREAKGAD || key == 'b')
  565.         signal = SIGBREAKF_CTRL_C;
  566.  
  567.     /*
  568.      *        We continue on if (somehow) it looks like the Signal submenu was
  569.      *        used, or if the Kill function has been activated. In both cases, we
  570.      *        make sure a task actually has been selected.
  571.      */
  572.     if ((signal || taskMenu == I_KILL || gadget == KILLGAD || key == 'k') && current)
  573.         {
  574.         /*
  575.          *        Since there are many keyboard shortcuts leading here, we visibly
  576.          *        depress the appropriate gadget if necessary.
  577.          */
  578.         if (key)
  579.             PressGadget(mainWindow, signal ? breakGad : killGad);
  580.  
  581.         /*
  582.          *        First we need to make sure the task is still valid, and that it
  583.          *        is not our own task!
  584.          */
  585.         BusyPointer();
  586.  
  587.         if (!ValidTask(currentTask))
  588.             {
  589.             SimpleRequest(&lost, NULL);
  590.             CreateList(&memoryKey);
  591.             }
  592.  
  593.         else if (currentTask == FindTask(NULL))
  594.             SimpleRequest(&suicide, NULL);
  595.  
  596.         else
  597.             {
  598.             /*
  599.              *        We're only allowed to continue on if:
  600.              *
  601.              *        - The Confirm Actions setting is not set, or;
  602.              *
  603.              *        - The Shift key is held down, or;
  604.              *
  605.              *        - The above two are false and the user answers Okay to a
  606.              *          confirmation requester.
  607.              *
  608.              *        This line of code makes use of the fact that C only
  609.              *        evaluates as much of a sequence of logical expressions as is
  610.              *        necessary to determine if the outcome will be true or false.
  611.              */
  612.             if (!confirm || shift ||
  613.                 SimpleRequest(signal ? &breakMsg : &killMsg, currentTask -> tc_Node.ln_Name))
  614.                 {
  615.                 /*
  616.                  *        At this point, it is possible that the target task has
  617.                  *        quietly exited. We handled that case at the start and
  618.                  *        notified the user appropriately, but here we'll just let
  619.                  *        it go and consider the job done. We'll disable task
  620.                  *        switching now and do the second check - we couldn't just
  621.                  *        have one check earlier since it wouldn't be possible to
  622.                  *        disable task switching while the confirmation requester
  623.                  *        was open!
  624.                  */
  625.                 Forbid();
  626.                 if (ValidTask(currentTask))
  627.                     {
  628.                     if (signal)
  629.                         {
  630.                         /*
  631.                          *        This is where the two functions differ. For
  632.                          *        Break, we send a certain signal to the task, and
  633.                          *        then wait around for half a second, giving it
  634.                          *        time to exit before we update the ListView.
  635.                          */
  636.                         Signal(currentTask, signal);
  637.                         Permit();
  638.                         Delay(BreakDelay);
  639.                         }
  640.                     else
  641.                         {
  642.                         /*
  643.                          *        This is the Kill code. (Note that we're still
  644.                          *        inside a Forbid().) The very first thing we do
  645.                          *        is kill the task, before re-enabling task
  646.                          *        switching...
  647.                          */
  648.                         RemTask(currentTask);  /* BLAM! */
  649.                         Permit();
  650.  
  651.                         /*
  652.                          *        Some tricky stuff follows. We want to find out
  653.                          *        if the task had any windows or screens open.
  654.                          *        This is done by walking through all windows on
  655.                          *        all screens, checking for message reply-ports
  656.                          *        pointing to the task. We build a linked list of
  657.                          *        these windows (since there could potentially be
  658.                          *        many of them), use a Remember structure (as with
  659.                          *        the task list) for the nodes, and lock
  660.                          *        IntuitionBase so that no window operations can
  661.                          *        happen while we traverse the lists.
  662.                          */
  663.                         windowList.lh_Head = (struct Node *)&windowList.lh_Tail;
  664.                         windowList.lh_Tail = NULL;
  665.                         windowList.lh_TailPred = (struct Node *)&windowList.lh_Head;
  666.                         windowKey = NULL;
  667.                         ibase = LockIBase(NULL);
  668.  
  669.                         /*
  670.                          *        The outer loop walks through all the screens...
  671.                          */
  672.                         for (screen = IntuitionBase -> FirstScreen; screen; screen = screen -> NextScreen)
  673.  
  674.                             /*
  675.                              *        The inner loop is for windows...
  676.                              */
  677.                             for (window = screen -> FirstWindow; window; window = window -> NextWindow)
  678.  
  679.                                 /*
  680.                                  *        We have a match if the window has a
  681.                                  *        valid UserPort, and the task referenced
  682.                                  *        there is the one we killed above.
  683.                                  */
  684.                                 if (window -> UserPort && (struct Task *)(window -> UserPort -> mp_SigTask) == currentTask)
  685.  
  686.                                     /*
  687.                                      *        For a match, we allocate memory for
  688.                                      *        a new Node structure to store a
  689.                                      *        pointer to the window. (If this
  690.                                      *        memory allocation fails, we'll just
  691.                                      *        move onto the next window - missing
  692.                                      *        out a window is the least of our
  693.                                      *        worries if we're *that* low on free
  694.                                      *        memory!) While the Node structure is
  695.                                      *        supposed to contain a pointer to
  696.                                      *        text, we cheat a little and stick in
  697.                                      *        a pointer to the Window structure.
  698.                                      */
  699.                                     if (node = AllocRemember(&windowKey, sizeof(struct Node), MEMF_ANY))
  700.                                         {
  701.                                         node -> ln_Name = (char *)window;
  702.                                         AddTail(&windowList, node);
  703.                                         }
  704.  
  705.                         /*
  706.                          *        At the end of the loops, we have a list of any
  707.                          *        windows the task had open. Assuming this list is
  708.                          *        not empty, we ask the user if he'd like to close
  709.                          *        them. Since we can't leave IntuitionBase locked
  710.                          *        from now on (a requester is being opened!), we
  711.                          *        simply have to hope the windows don't get closed
  712.                          *        behind our backs, as then the machine will crash
  713.                          *        when we go to close them... (Sorry, I can't see
  714.                          *        any way around this!)
  715.                          */
  716.                         UnlockIBase(ibase);
  717.                         if (windowList.lh_Head -> ln_Succ)  /* at least one entry here */
  718.                             {
  719.                             if (SimpleRequest(&closeWindows, NULL))
  720.                                 {
  721.                                 /*
  722.                                  *        Now we go round in a loop closing all
  723.                                  *        the windows on the list. As we do so,
  724.                                  *        we remove the Node structures, *unless*
  725.                                  *        the window is the last on its screen, in
  726.                                  *        which case we leave the Node intact, but
  727.                                  *        replace its data with the screen pointer
  728.                                  *        instead - see below for why!
  729.                                  */
  730.                                 node = windowList.lh_Head;
  731.                                 while (nextNode = node -> ln_Succ)
  732.                                     {
  733.                                     screen = (window = (struct Window *)(node -> ln_Name)) -> WScreen;
  734.                                     CloseWindowSafely(window);
  735.                                     if (screen -> FirstWindow)  /* more left on this screen */
  736.                                         Remove(node);
  737.                                     else
  738.                                         node -> ln_Name = (char *)screen;
  739.  
  740.                                     /*
  741.                                      *        "node" is probably invalid (freed)
  742.                                      *        down here, but we got a copy of its
  743.                                      *        successor earlier...
  744.                                      */
  745.                                     node = nextNode;
  746.                                     }
  747.  
  748.                                 /*
  749.                                  *        At this point, if the list is not empty,
  750.                                  *        it contains pointers to empty screens.
  751.                                  *        So we ask the user if he wants to close
  752.                                  *        these too, on the (reasonable)
  753.                                  *        assumption that they belong to the task
  754.                                  *        we killed.
  755.                                  */
  756.                                 if (windowList.lh_Head -> ln_Succ)  /* at least one entry here */
  757.                                     if (SimpleRequest(&closeScreens, NULL))
  758.                                         FORLIST(&windowList, node)
  759.                                             CloseScreen((struct Screen *)(node -> ln_Name));
  760.                                 }
  761.                             }
  762.  
  763.                         /*
  764.                          *        Down here, we've finished playing about with
  765.                          *        window pointers, so we can free up the list.
  766.                          */
  767.                         FreeRemember(&windowKey, TRUE);
  768.                         }
  769.                     }
  770.  
  771.                 /*
  772.                  *        At the end of it all, we update the list to reflect what
  773.                  *        happened.
  774.                  */
  775.                 CreateList(&memoryKey);
  776.                 }
  777.             }
  778.         NormalPointer();
  779.         }
  780.  
  781.     /*
  782.      *        Here is the code for the Frozen menu item. It actually consists of
  783.      *        some "heavy magic". We create two new Exec task states for frozen
  784.      *        tasks: one to say "frozen while sleeping" and the other to say
  785.      *        "frozen while awake" (so we know whether the task needs to run when
  786.      *        melted). Since these states are actually invalid, Exec will ignore
  787.      *        the frozen tasks. All this has to be done while interrupts are
  788.      *        disabled, since we might be moving tasks between lists.
  789.      */
  790.     if (taskMenu == I_FROZEN)
  791.         {
  792.         Disable();
  793.  
  794.         /*
  795.          *        As with the Break and Kill code, we must make sure that the task
  796.          *        is still valid, and is not us!
  797.          */
  798.         if (!ValidTask(currentTask))
  799.             {
  800.             Enable();
  801.             SimpleRequest(&lost, NULL);
  802.             CreateList(&memoryKey);
  803.             }
  804.  
  805.         else if (currentTask == FindTask(NULL))
  806.             {
  807.             Enable();
  808.             SimpleRequest(&suicide, NULL);
  809.             }
  810.  
  811.         else
  812.             {
  813.             /*
  814.              *        We look at the state of the Frozen menu item, and if it's
  815.              *        checked, continue if the task has not already been frozen.
  816.              */
  817.             if (ItemAddress(menuStrip, FULLMENUNUM(M_TASK, I_FROZEN, NOSUB)) -> Flags & CHECKED)
  818.                 {
  819.                 if (!Frozen(currentTask))
  820.  
  821.                     /*
  822.                      *        Freezing the task depends on what state it's in.
  823.                      *        Tasks waiting for CPU time (TS_READY) must get put
  824.                      *        on the "sleeping" list, as well as changing their
  825.                      *        state.
  826.                      */
  827.                     if (currentTask -> tc_State & TS_READY)
  828.                         {
  829.                         Remove((struct Node *)currentTask);
  830.                         Enqueue(&(SysBase -> TaskWait), (struct Node *)currentTask);
  831.                         currentTask -> tc_State = FROZENREADY;
  832.                         }
  833.                     else
  834.                         currentTask -> tc_State = FROZEN;
  835.                 }
  836.  
  837.             /*
  838.              *        Task must be melted. Again, there are two cases: if it's
  839.              *        ready to run, we must move it back onto its original list
  840.              *        before changing its state; if it's sleeping, we send it a
  841.              *        signal based on the contents of its "received signals" field
  842.              *        (if a signal arrived while it was frozen, it would not have
  843.              *        been woken up since it was in an invalid state!).
  844.              */
  845.             else if (currentTask -> tc_State == FROZENREADY)
  846.                 {
  847.                 Remove((struct Node *)currentTask);
  848.                 Enqueue(&(SysBase -> TaskReady), (struct Node *)currentTask);
  849.                 currentTask -> tc_State = TS_READY;
  850.                 }
  851.             else if (currentTask -> tc_State == FROZEN)
  852.                 {
  853.                 currentTask -> tc_State = TS_WAIT;
  854.                 Signal(currentTask, currentTask -> tc_SigRecvd);
  855.                 }
  856.  
  857.             Enable();
  858.  
  859.             /*
  860.              *        Down here, we update the ListView to reflect the state of
  861.              *        the task, in a similar way to when we changed its priority
  862.              *        above.
  863.              */
  864.             GT_SetGadgetAttrs(listGad, mainWindow, NULL,
  865.                                 GTLV_Labels,    ~0,
  866.                                 TAG_END);
  867.             CreateString(currentTask, current -> ln_Name);
  868.             GT_SetGadgetAttrs(listGad, mainWindow, NULL,
  869.                                 GTLV_Labels,    &taskList,
  870.                                 GTLV_Selected,    pos,  /* only required under 2.0 */
  871.                                 TAG_END);
  872.             }
  873.         }
  874.  
  875.     /*
  876.      *        It is possible for the user to play around with the Frozen menu item
  877.      *        without us noticing (e.g. it's followed by another menu selection,
  878.      *        and so didn't get handled above). We do a quick check to see if the
  879.      *        checkmark has got out of sync with the state of the task (assuming
  880.      *        one has been selected!), and if so, put it back to the way it should
  881.      *        be. (We could just update the checkmark all the time, but that would
  882.      *        be wasteful since almost every time HandleMainWindow() was called we
  883.      *        would be modifying the menu!)
  884.      */
  885.     else if (current)
  886.         {
  887.         item = ItemAddress(menuStrip, FULLMENUNUM(M_TASK, I_FROZEN, NOSUB));
  888.         frozen = Frozen(currentTask);
  889.  
  890.         switch ((item -> Flags & CHECKED) != 0)
  891.             {
  892.             case TRUE:
  893.                 if (!frozen)
  894.                     {
  895.                     ClearMenuStrip(mainWindow);
  896.                     item -> Flags &= ~CHECKED;
  897.                     ResetMenuStrip(mainWindow, menuStrip);
  898.                     }
  899.                 break;
  900.  
  901.             case FALSE:
  902.                 if (frozen)
  903.                     {
  904.                     ClearMenuStrip(mainWindow);
  905.                     item -> Flags |= CHECKED;
  906.                     ResetMenuStrip(mainWindow, menuStrip);
  907.                     }
  908.                 break;
  909.             }
  910.         }
  911.  
  912.     /*
  913.      *        Now we're onto the Project menu, and first up is opening the
  914.      *        Settings window. If it's already open, we move it to the front.
  915.      */
  916.     if (gadget == SETTINGSGAD || projectMenu == I_SETTINGS || key == 's')
  917.         {
  918.         if (key)
  919.             PressGadget(mainWindow, setGad);
  920.  
  921.         if (setWindow)
  922.             {
  923.             WindowToFront(setWindow);
  924.             ActivateWindow(setWindow);
  925.             }
  926.         else
  927.             OpenSettingsWindow();
  928.         }
  929.  
  930.     /*
  931.      *        Here is the rather trivial code for the About menu item. If the user
  932.      *        selects the Help button in the requester, we open up the Help file.
  933.      */
  934.     if (projectMenu == I_ABOUT)
  935.         if (!SimpleRequest(&about, NULL))
  936.             Help();
  937.  
  938.     /*
  939.      *        If the Help menu item is selected, or the Help key is pressed, we
  940.      *        also open up the Help file.
  941.      */
  942.     if (projectMenu == I_HELP || raw == HELP)
  943.         Help();
  944.  
  945.     /*
  946.      *        The code for the Hide menu item just calls the Hide() function,
  947.      *        which either commences the exit routine, or else closes all the
  948.      *        windows and causes PriMan to sit quietly in the background.
  949.      */
  950.     if (projectMenu == I_HIDE)
  951.         Hide();
  952.  
  953.     /*
  954.      *        Quitting is trivial - we just need to assert ALL_OKAY!
  955.      */
  956.     if (projectMenu == I_QUIT || key == ESCAPE)
  957.         error = ALL_OKAY;
  958.     }
  959.  
  960.  
  961. /*
  962.  *        Process an input event from the settings window. We have to be careful
  963.  *        about which gadget page is showing!
  964.  */
  965. void HandleSettingsWindow(ULONG class, WORD code, WORD key, WORD raw, UWORD shift, struct Gadget *selectedGad)
  966.     {
  967.     /*
  968.      *        When writing out the ToolTypes, we need the following:
  969.      *
  970.      *        oldTools = pointer to existing ToolType array (which itself contains
  971.      *                   pointers to the ToolTypes themselves)
  972.      *
  973.      *        newTools = array of pointers to new ToolType array
  974.      *
  975.      *        empty     = array of strings making up the standard ToolTypes,
  976.      *                   pointed to from newTools
  977.      *
  978.      *        string     = existing ToolType we're currently working on
  979.      */
  980.     char                **oldTools,
  981.                         *newTools[MaxToolTypes],
  982.                         empty[MaxStdTools][MAXFONTNAME + MaxToolName],
  983.                         *string;
  984.  
  985.     int                    oldPos,                /* current position in original ToolType array    */
  986.                         newPos;                /* current position in our new ToolType array    */
  987.  
  988.     UWORD                gadget;                /* gadget that was pressed                        */
  989.     WORD                newPage;            /* settings page we're going to move to            */
  990.     BYTE                toolpri;            /* PriMan's task priority                        */
  991.     BOOL                redraw = FALSE;        /* main window will need redrawing later        */
  992.  
  993.     gadget = class == GADGETUP ? selectedGad -> GadgetID : 0;
  994.  
  995.     /*
  996.      *        First we handle events that can happen regardless of which gadget
  997.      *        page is showing. The REFRESHWINDOW event is slightly more complex
  998.      *        than with the main window, because the line around the gadgets needs
  999.      *        to get redrawn (since it isn't a GadTools gadget, which refresh
  1000.      *        themselves).
  1001.      */
  1002.     if (class == IDCMP_REFRESHWINDOW)
  1003.         {
  1004.         GT_BeginRefresh(setWindow);
  1005.         DrawSettingsBox();
  1006.         GT_EndRefresh(setWindow, TRUE);
  1007.         }
  1008.  
  1009.     /*
  1010.      *        As before, pressing the Escape key immediately aborts.
  1011.      */
  1012.     else if (key == ESCAPE)
  1013.         error = ALL_OKAY;
  1014.  
  1015.     /*
  1016.      *        If the Help key is pressed, we open up the Help file.
  1017.      */
  1018.     else if (raw == HELP)
  1019.         Help();
  1020.  
  1021.     /*
  1022.      *        If the Page cycle gadget is clicked, we just move onto the new page.
  1023.      */
  1024.     else if (gadget == PAGEGAD)
  1025.         NewPage(code);
  1026.  
  1027.     /*
  1028.      *        The keyboard shortcut requires a bit more work. We need to update
  1029.      *        the Page cycle gadget ourselves, and also check to see if the Shift
  1030.      *        key is being pressed, before moving onto the new page.
  1031.      */
  1032.     else if (key == 'p')
  1033.         {
  1034.         newPage = Step(page, 2, shift);
  1035.         GT_SetGadgetAttrs(pageGad, setWindow, NULL,
  1036.                             GTCY_Active, newPage,
  1037.                             TAG_END);
  1038.         NewPage(newPage);
  1039.         }
  1040.  
  1041.     /*
  1042.      *        Save and Use are pretty similar, and share a lot of code. Note that
  1043.      *        we don't do anything if the user presses S for Save but PriMan's
  1044.      *        .info file cannot be found, since there is no icon to write out the
  1045.      *        ToolTypes to!
  1046.      */
  1047.     else if (gadget == SAVEGAD || (key == 's' && myIcon) ||
  1048.              gadget == USEGAD || key == 'u')
  1049.         {
  1050.         if (key)
  1051.             PressGadget(setWindow, key == 's' ? saveGad : useGad);
  1052.     
  1053.         BusyPointer();  /* there may be a delay writing out to disk */
  1054.  
  1055.         /*
  1056.          *        Now we start copying all the temporary settings variables into
  1057.          *        the master variables. If the user did something like change the
  1058.          *        window type or font, we'll need to redraw the window later, so
  1059.          *        we use another variable to remind us about that.
  1060.          *
  1061.          *        First up are the Commodity settings. We update the hotkey
  1062.          *        variable directly from the Hotkey string gadget, and the
  1063.          *        priority from the Priority integer gadget.
  1064.          */
  1065.         strcpy(hotkey, ((struct StringInfo *)hotkeyGad -> SpecialInfo) -> Buffer);
  1066.         priority = ((struct StringInfo *)priorityGad -> SpecialInfo) -> LongInt;
  1067.  
  1068.         /*
  1069.          *        If PriMan was already running as a Commodity, we'll remove it.
  1070.          *        Then if it still needs to run as a Commodity, it gets added back
  1071.          *        in. This effectively changes its hotkey and priority, without
  1072.          *        us having to go to the effort of checking to see if this really
  1073.          *        needs to get done. :-)
  1074.          */
  1075.         if (commodity)
  1076.             {
  1077.             DeleteCxObjAll(broker);
  1078.             broker = NULL;
  1079.             }
  1080.     
  1081.         if (tempCommodity)
  1082.             SetupCommodity();
  1083.  
  1084.         /*
  1085.          *        If the window type has been changed, we make a note that we'll
  1086.          *        need to redraw ourselves.
  1087.          */
  1088.         if (refresh != tempRefresh)
  1089.             {
  1090.             refresh = tempRefresh;
  1091.             redraw = TRUE;
  1092.             }
  1093.  
  1094.         /*
  1095.          *        If the user has changed the Confirm Actions setting, we need to
  1096.          *        add or remove "..." after the Kill and Signal menu items.
  1097.           */
  1098.         if (confirm != tempConfirm)
  1099.             {
  1100.             confirm = tempConfirm;
  1101.             MenuEllipsis(menuStrip, TRUE);
  1102.             }
  1103.  
  1104.         /*
  1105.          *        We'll copy over the rest of the boolean variables now.
  1106.          */
  1107.         open        = tempOpen;    
  1108.         iconify        = tempIconify;
  1109.         commodity    = tempCommodity;
  1110.         popup        = tempPopup;
  1111.  
  1112.         /*
  1113.          *        This is the priority the user has entered in the Task Priority
  1114.          *        integer gadget. Since it is this one that's going to get used,
  1115.          *        we check against the current priority PriMan is running at, and
  1116.          *        if that's different, change it and update the ListView to
  1117.          *        reflect the new priority (this is easier than just finding our
  1118.          *        own entry and changing that!).
  1119.          */
  1120.         toolpri = ((struct StringInfo *)toolpriGad -> SpecialInfo) -> LongInt;
  1121.         if (FindTask(NULL) -> tc_Node.ln_Pri != toolpri)
  1122.             {
  1123.             SetTaskPri(FindTask(NULL), toolpri);
  1124.             CreateList(&memoryKey);
  1125.             }
  1126.  
  1127.         /*
  1128.          *        If the Gadget Font requester was opened, and the new font is not
  1129.          *        the same as the old one, we copy it across and make a note to
  1130.          *        redraw the window.
  1131.          */
  1132.         if (newPropFont && (strcmp((propFontReq -> fo_Attr).ta_Name, propName)
  1133.                             || !((propFontReq -> fo_Attr).ta_YSize == propTA.ta_YSize)))
  1134.             {
  1135.             strcpy(propName, (propFontReq -> fo_Attr).ta_Name);
  1136.             propTA.ta_YSize = (propFontReq -> fo_Attr).ta_YSize;
  1137.             redraw = TRUE;
  1138.             }
  1139.     
  1140.         /*
  1141.          *        Ditto for the List Font.
  1142.          */
  1143.         if (newMonoFont && (strcmp((monoFontReq -> fo_Attr).ta_Name, monoName)
  1144.                             || !((monoFontReq -> fo_Attr).ta_YSize == monoTA.ta_YSize)))
  1145.             {
  1146.             strcpy(monoName, (monoFontReq -> fo_Attr).ta_Name);
  1147.             monoTA.ta_YSize = (monoFontReq -> fo_Attr).ta_YSize;
  1148.             redraw = TRUE;
  1149.             }
  1150.  
  1151.         /*
  1152.          *        Finally, the user may have changed the Iconify or Commodity
  1153.          *        options, meaning the Hide menu item needs enabling or disabling.
  1154.          *        We handle that here. If PriMan can either be iconified or run in
  1155.          *        the background as a Commodity, it gets enabled; otherwise, it is
  1156.          *        disabled.
  1157.          */
  1158.         if (iconify || commodity)
  1159.             OnMenu(mainWindow, FULLMENUNUM(M_PROJECT, I_HIDE, NOSUB));
  1160.         else
  1161.             OffMenu(mainWindow, FULLMENUNUM(M_PROJECT, I_HIDE, NOSUB));
  1162.  
  1163.         /*
  1164.          *        Here is where the Save and Use options differ. The former
  1165.          *        requires writing everything out to disk. We already have a
  1166.          *        pointer to PriMan's .info file (from when we read the ToolTypes
  1167.          *        in the first place), and we reuse that.
  1168.          */
  1169.         if (gadget == SAVEGAD || key == 's')
  1170.             {
  1171.             /*
  1172.              *        Within the .info file is a pointer to the ToolTypes. We
  1173.              *        make a copy of this in oldTool so it can be restored
  1174.              *        later, and use it to walk through the ToolTypes.
  1175.              */
  1176.             oldTools = myIcon -> do_ToolTypes;
  1177.  
  1178.             /*
  1179.              *        What we want to do is create a new array of ToolTypes,
  1180.              *        containing all the "standard" ones, along with any
  1181.              *        additional ones the user may have put in. The way we do
  1182.              *        this is: we walk through the original array, copy the
  1183.              *        pointers to unrecognised ToolTypes into our new array,
  1184.              *        and then tag on the standard ToolTypes. Essentially we
  1185.              *        are skipping over the standard ToolTypes in the original
  1186.              *        array, as they are going to be replaced later.
  1187.              *
  1188.              *        The alternative would be to simply overwrite anything
  1189.              *        already in the array, but that's not entirely user-
  1190.              *        friendly... (If the user has added ToolTypes manually since
  1191.              *        starting PriMan, they'll get overwritten anyway, but that's
  1192.              *        just tough!)
  1193.              *
  1194.              *        The loop continues through the original array until it
  1195.              *        encounters a NULL pointer. Each iteration does a string
  1196.              *        comparison against the standard ToolTypes, and if none
  1197.              *        of them match, a pointer to the string is copied over.
  1198.              *        The loop can end prematurely if we run out of space in
  1199.              *        the array to hold everything. (The array can hold
  1200.              *        MaxToolTypes entries, of which we'll be using
  1201.              *        MaxStdTools entries, plus one for the NULL pointer at
  1202.              *        the end.)
  1203.              */
  1204.             oldPos = newPos = 0;
  1205.  
  1206.             while ((string = oldTools[oldPos++]) && newPos < MaxToolTypes - MaxStdTools - 1)
  1207.                 if (strncmp(string, "COMMODITY", 9) && strncmp(string, "CX_POPUP", 8)
  1208.                     && strncmp(string, "CX_POPKEY", 9) && strncmp(string, "CX_PRIORITY", 11)
  1209.                     && strncmp(string, "LEFT", 4) && strncmp(string, "TOP", 3)
  1210.                     && strncmp(string, "WIDTH", 5) && strncmp(string, "HEIGHT", 6)
  1211.                     && strncmp(string, "GADFONT", 7) && strncmp(string, "GADSIZE", 7)
  1212.                     && strncmp(string, "LISTFONT", 8) && strncmp(string, "LISTSIZE", 8)
  1213.                     && strncmp(string, "REFRESH", 7) && strncmp(string, "SCREEN", 6)
  1214.                     && strncmp(string, "ICONLEFT", 8) && strncmp(string, "ICONTOP", 7)
  1215.                     && strncmp(string, "CONFIRM", 7) && strncmp(string, "ICONIFY", 7)
  1216.                     && strncmp(string, "TOOLPRI", 7))  /* get rid of our ones */
  1217.                     newTools[newPos++] = string;
  1218.  
  1219.             /*
  1220.              *        Now for the standard ToolTypes. We build this up in a
  1221.              *        separate array, which has plenty of space allocated for
  1222.              *        it to hold the strings.
  1223.              */
  1224.             sprintf(empty[0], "COMMODITY=%s", commodity ? "YES" : "NO");
  1225.             sprintf(empty[1], "CX_POPUP=%s", popup ? "YES" : "NO");
  1226.             sprintf(empty[2], "CX_POPKEY=%s", hotkey);
  1227.             sprintf(empty[3], "CX_PRIORITY=%ld", priority);
  1228.             sprintf(empty[4], "LEFT=%ld", mainWindow -> LeftEdge);
  1229.             sprintf(empty[5], "TOP=%ld", mainWindow -> TopEdge);
  1230.             sprintf(empty[6], "WIDTH=%ld", mainWindow -> Width);
  1231.             sprintf(empty[7], "HEIGHT=%ld", mainWindow -> Height);
  1232.             sprintf(empty[8], "GADFONT=%s", propName);
  1233.             sprintf(empty[9], "GADSIZE=%ld", propTA.ta_YSize);
  1234.             sprintf(empty[10], "LISTFONT=%s", monoName);
  1235.             sprintf(empty[11], "LISTSIZE=%ld", monoTA.ta_YSize);
  1236.             sprintf(empty[12], "REFRESH=%s", refresh == SIMPLEWINDOW ? "SIMPLE" : "SMART");
  1237.             sprintf(empty[13], "SCREEN=%s", open == FRONTSCREEN ? "FRONT" : "DEFAULT");
  1238.             sprintf(empty[14], "ICONLEFT=%ld", iconLeft);
  1239.             sprintf(empty[15], "ICONTOP=%ld", iconTop);
  1240.             sprintf(empty[16], "CONFIRM=%s", confirm ? "YES" : "NO");
  1241.             sprintf(empty[17], "ICONIFY=%s", iconify ? "YES" : "NO");
  1242.             sprintf(empty[18], "TOOLPRI=%ld", toolpri);
  1243.  
  1244.             /*
  1245.              *        The string pointers in the above array get copied into
  1246.              *        newTools, starting just after where we put the last non-
  1247.              *        standard ToolType. (oldPos gets reused here.)
  1248.              */
  1249.             for (oldPos = 0; oldPos < MaxStdTools;)
  1250.                 newTools[newPos++] = empty[oldPos++];
  1251.  
  1252.             /*
  1253.              *        Finally, we put a NULL pointer after the last entry.
  1254.              */
  1255.             newTools[newPos] = NULL;
  1256.  
  1257.             /*
  1258.              *        Now we're ready to write out the .info file again. We
  1259.              *        first replace the pointer to the original ToolType array
  1260.              *        with our new one.
  1261.              */
  1262.             myIcon -> do_ToolTypes = newTools;
  1263.             PutDiskObject(myName, myIcon);
  1264.  
  1265.             /*
  1266.              *        Before ending, we restore the pointer to the original
  1267.              *        ToolType array, as it will be freed later (when PriMan
  1268.              *        exits).
  1269.              */
  1270.             myIcon -> do_ToolTypes = oldTools;
  1271.             }
  1272.  
  1273.         NormalPointer();
  1274.  
  1275.         /*
  1276.          *        Down here, all the settings have been updated, and - if
  1277.          *        necessary - written out to disk. So we close the Settings
  1278.          *        window, and then (if required) redraw the main window.
  1279.          */
  1280.         CloseSettingsWindow();
  1281.         if (redraw)
  1282.             {
  1283.             CloseMainWindow();
  1284.             OpenMainWindow();
  1285.             }
  1286.         }
  1287.  
  1288.     /*
  1289.      *        Handling the Cancel gadget is much easier. (Note that even though
  1290.      *        the Settings window does not have a close gadget, it can still
  1291.      *        receive the corresponding event through - say - WindowDaemon.)
  1292.      */
  1293.     else if (gadget == CANCELGAD || key == 'c' || class == IDCMP_CLOSEWINDOW)
  1294.         {
  1295.         if (key)
  1296.             PressGadget(setWindow, cancelGad);
  1297.  
  1298.         /*
  1299.          *        Since the master settings variables have not been changed, all
  1300.          *        we need to do is close the window!
  1301.          */
  1302.         CloseSettingsWindow();
  1303.         }
  1304.  
  1305.     /*
  1306.      *        Now we're onto the page-specific gadgets. We only want to handle the
  1307.      *        ones pertaining to the currently-selected page. (For example, the
  1308.      *        same keypress can have a different meaning depending on the page
  1309.      *        being shown.)
  1310.      */
  1311.     else switch(page)
  1312.         {
  1313.         case 0:
  1314.             /*
  1315.              *        The Interface page. First up is the Gadget Font button. A
  1316.              *        separate function handles most of this for us.
  1317.              */
  1318.             if (gadget == GFONTGAD || key == 'g')
  1319.                 {
  1320.                 if (key)
  1321.                     PressGadget(setWindow, propFontButGad);
  1322.  
  1323.                 newPropFont = RequestFont(&propFontReq, monoFontReq, tempPropName, &tempPropSize,
  1324.                                             propFontGad, propString, "Select Gadget Font", 0);
  1325.                 }
  1326.  
  1327.             /*
  1328.              *        The List Font button is handled in exactly the same manner,
  1329.              *        except it only allows fixed-width fonts.
  1330.              */            
  1331.             else if (gadget == LFONTGAD || key == 'l')
  1332.                 {
  1333.                 if (key)
  1334.                     PressGadget(setWindow, monoFontButGad);
  1335.  
  1336.                 newMonoFont = RequestFont(&monoFontReq, propFontReq, tempMonoName, &tempMonoSize,
  1337.                                             monoFontGad, monoString, "Select List Font", FOF_FIXEDWIDTHONLY);
  1338.                 }
  1339.  
  1340.             /*
  1341.              *        Handling the Window Type gadget is easy if the user clicks
  1342.              *        on the cycle gadget - we just copy across the new position.
  1343.              */
  1344.             else if (gadget == REFRESHGAD)
  1345.                 tempRefresh = code;
  1346.  
  1347.             /*
  1348.              *        The keyboard shortcut requires us to change the cycle gadget
  1349.              *        ourselves, and toggle the settings variable.
  1350.              */
  1351.             else if (key == 'w')
  1352.                 {
  1353.                 tempRefresh = !tempRefresh;
  1354.                 GT_SetGadgetAttrs(refreshGad, setWindow, NULL,
  1355.                                     GTCY_Active, tempRefresh,
  1356.                                     TAG_END);
  1357.                 }
  1358.  
  1359.             /*
  1360.              *        The code for the Open On gadget is similar...
  1361.              */
  1362.             else if (gadget == OPENGAD)
  1363.                 tempOpen = code;
  1364.  
  1365.             else if (key == 'o')
  1366.                 {
  1367.                 tempOpen = !tempOpen;
  1368.                 GT_SetGadgetAttrs(openGad, setWindow, NULL,
  1369.                                     GTCY_Active, tempOpen,
  1370.                                     TAG_END);
  1371.                 }
  1372.  
  1373.             break;
  1374.  
  1375.         case 1:
  1376.             /*
  1377.              *        The Commodity page. First up is the Install checkbox.
  1378.              *        Updating this is similar to the cycle gadgets above, except
  1379.              *        we must also enable or disable the rest of the gadgets on
  1380.              *        this page, based on the new setting.
  1381.              */
  1382.             if (gadget == COMGAD || key == 'i')
  1383.                 {
  1384.                 if (gadget == COMGAD)
  1385.                     tempCommodity = selectedGad -> Flags & GFLG_SELECTED;
  1386.                 else
  1387.                     {
  1388.                     tempCommodity = !tempCommodity;
  1389.                     GT_SetGadgetAttrs(comGad, setWindow, NULL,
  1390.                                         GTCB_Checked,    tempCommodity,
  1391.                                         TAG_END);
  1392.                     }
  1393.             
  1394.                 GT_SetGadgetAttrs(popupGad, setWindow, NULL,
  1395.                                     GA_Disabled,    !tempCommodity,
  1396.                                     TAG_END);
  1397.                 GT_SetGadgetAttrs(hotkeyGad, setWindow, NULL,
  1398.                                     GA_Disabled,    !tempCommodity,
  1399.                                     TAG_END);
  1400.                 GT_SetGadgetAttrs(priorityGad, setWindow, NULL,
  1401.                                     GA_Disabled,    !tempCommodity,
  1402.                                     TAG_END);
  1403.                 }
  1404.  
  1405.             /*
  1406.              *        Again, the Popup checkbox is handled similarly...
  1407.              */
  1408.             else if (gadget == POPUPGAD)
  1409.                 tempPopup = selectedGad -> Flags & GFLG_SELECTED;
  1410.             
  1411.             else if (key == 'l' && tempCommodity)
  1412.                 {
  1413.                 tempPopup = !tempPopup;
  1414.                 GT_SetGadgetAttrs(popupGad, setWindow, NULL,
  1415.                                     GTCB_Checked,    tempPopup,
  1416.                                     TAG_END);
  1417.                 }
  1418.  
  1419.             /*
  1420.              *        The hotkey for the string and integer gadgets simply places
  1421.              *        the cursor inside them.
  1422.              */
  1423.             else if (key == 'h' && tempCommodity)
  1424.                 ActivateGadget(hotkeyGad, setWindow, NULL);
  1425.             
  1426.             else if (key == 'y' && tempCommodity)
  1427.                 ActivateGadget(priorityGad, setWindow, NULL);
  1428.  
  1429.             break;
  1430.  
  1431.         case 2:
  1432.             /*
  1433.              *        The General page. It works in a similar way to before - the
  1434.              *        hotkey for the integer gadget places the cursor inside it,
  1435.              *        and the checkboxes get toggled on their hotkeys.
  1436.              */
  1437.             if (key == 'y')
  1438.                 ActivateGadget(toolpriGad, setWindow, NULL);
  1439.  
  1440.             else if (gadget == CONFIRMGAD)
  1441.                 tempConfirm = selectedGad -> Flags & GFLG_SELECTED;
  1442.             
  1443.             else if (key == 'a')
  1444.                 {
  1445.                 tempConfirm = !tempConfirm;
  1446.                 GT_SetGadgetAttrs(confirmGad, setWindow, NULL,
  1447.                                     GTCB_Checked,    tempConfirm,
  1448.                                     TAG_END);
  1449.                 }
  1450.  
  1451.             else if (gadget == ICONIFYGAD)
  1452.                 tempIconify = selectedGad -> Flags & GFLG_SELECTED;
  1453.  
  1454.             else if (key == 'i')
  1455.                 {
  1456.                 tempIconify = !tempIconify;
  1457.                 GT_SetGadgetAttrs(iconifyGad, setWindow, NULL,
  1458.                                     GTCB_Checked,    tempIconify,
  1459.                                     TAG_END);
  1460.                 }
  1461.  
  1462.             break;
  1463.         }
  1464.     }
  1465.